﻿using System;
using System.IO;

namespace Gmantis.Zip
{
    public class ZipEntryStreamWriter : ZStreamWriter
    {
        // Fields
        private bool _cancel;
        private DateTime _dateTime;
        private string _entryName;
        private string _fileName;
        private ZipFile _owner;
        private string _tempFile;

        // Methods
        internal ZipEntryStreamWriter(ZipFile zipFile, string fileName, string entryName, DateTime dateTime, bool memory)
        {
            Stream stream;
            if (memory)
            {
                this._tempFile = null;
                stream = new MemoryStream();
            }
            else
            {
                this._tempFile = zipFile._tempFileName;
                if ((this._tempFile == null) || (this._tempFile.Length == 0))
                {
                    this._tempFile = Path.GetTempFileName();
                }
                stream = new FileStream(this._tempFile, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
            }
            this._owner = zipFile;
            this._entryName = entryName;
            this._fileName = fileName;
            this._dateTime = dateTime;
            this._cancel = false;
            base.InitStream(stream, zipFile.CompressionLevel, false, true);
            base.OwnsBaseStream = false;
        }

        private void AddCompressed()
        {
            ZipEntry item = new ZipEntry(this._owner);
            item.SetNameAndFlags(this._fileName, this._entryName, this._dateTime);
            int index = this._owner.Entries.IndexOf(item.FileName);
            if (index >= 0)
            {
                this._owner.Remove(index);
            }
            Stream fs = null;
            try
            {
                fs = this._owner.OpenInternal(this._owner.FileName);
                this._owner.Entries.Add(item);
                item._offset = this._owner._bytesBeforeZip + this._owner._offset;
                fs.SetLength(item._offset);
                item._szComp = base.SizeCompressedLong + (item.IsEncrypted ? ((long)12) : ((long)0));
                item._szOriginal = base.SizeUncompressedLong;
                fs.Position = item._offset;
                item.WriteLocalHeader(fs);
                Stream baseStream = base.BaseStream;
                baseStream.Position = 0L;
                if (item.IsEncrypted)
                {
                    CryptStream dstStream = new CryptStream(item, fs);
                    byte[] buf = new byte[12];
                    long crc = item._modTime << 0x10;
                    dstStream.CryptCryptHeader(crc, buf);
                    fs.Write(buf, 0, buf.Length);
                    ZipFile.StreamCopy(dstStream, baseStream, (long)((uint)baseStream.Length));
                    item._szComp = base.SizeCompressedLong + 12L;
                }
                else
                {
                    ZipFile.StreamCopy(fs, baseStream, (long)((uint)baseStream.Length));
                    item._szComp = base.SizeCompressedLong;
                }
                item._szOriginal = base.SizeUncompressedLong;
                item._crc32 = base.Checksum;
                fs.Position = item._offset;
                item.WriteLocalHeader(fs);
                fs.Position = fs.Length;
                this._owner._offset = fs.Position;
                this._owner.WriteCentralDir(fs);
            }
            finally
            {
                this._owner.CloseInternal(fs);
            }
        }

        public void Cancel()
        {
            this._cancel = true;
        }

        public override void Close()
        {
            base.Close();
            try
            {
                if (!this._cancel)
                {
                    base.BaseStream.Position = 0L;
                    this.AddCompressed();
                }
            }
            finally
            {
                base.BaseStream.Close();
                if (base.BaseStream is FileStream)
                {
                    File.Delete(this._tempFile);
                }
            }
        }
    }
}
